ABSTRACT: En el presente documento se presenta el trabajo que realice durante mi estancia en la becaria de la Escuela de Negocios del ITESO, la cual fue coordinada por el profesor: Jose DelaCerda Gastelum y la asistente de direccion: Dora Alicia Castaneda Corona.
Analisis Estadistico / Con R
\(Iniciamos\) \(con\) \(el\) \(codigo:\)
Se cargan las paqueterias con las cuales trabajara el programa, si estas no se cargan, pueden presentarse mutliples fallas ya que son necesarias para obtener ciertos resultados.
Primeramente es importante mencionar que el el setwd de “R” debe de estar en la ubicacion de la computadora en la cual se tienen todos los documentos de “Excel” como se muestra a continuacion:
En este caso se presenta la ubicacion en donde se encontraban los archivos en mi computadora:
# Elegimos el directorio en donde vamos a trabajar
setwd("~/Documents/ALEJANDRO RAMOS GUTIERREZ/PROYECTOS/StockX") # Donde estan nuestros archivos ubicados en el PC
En la siguiente linea se lee el archivo de Excel de las ventas (“Pruebar”) y se guarda en la variable llamada “Datos” el cual es convertido a un DataFrame, un tipo de archivo en “R” que emula una hoja de “Excel” y hace facil su manipulacion y analisis:
#-------------------------------------- IMPORTAR DATOS, ACOMODO DE DATOS ------------------------------------------
# -- Leemos la base de datos de las ventas de las Empresas
Datos <- readxl::read_excel("Stockx.xlsx", sheet = 1)
-
/
Datos <- as.data.frame(Datos)
A continuacion, como la base de datos leida contiene un 0 en donde no se tienen registro de las ventas, estos son convertidos en NA values, es decir en valores no disponibles, de esta manera evitamos realizar un analisis erroneo de los datos.
Posteriormente, como en “Datos” tambien se encontraban las variables “NAICS”, “PROPIEDAD” y “REGISTRO” de cada una de las empresas y pudieran presentar algun ruido en los datos cuando se trabajara con ellos, estas fueron eliminadas de la variable “Datos_anual” y de esta manera se quedarian unicamente las ventas de las empresas.
# -- Modelos de tenis
Sneaker_models <- unique(Datos$Sneaker_Name)
Sneaker_models_abb <- c('350 V2 Beluga','350 V2 Black Cooper','350 V2 Black Green','350 V2 Black Red','350 V2 Black Red 2017','350 V2 Black White',
'350 V2 Cream White','350 V2 Zebra','350 Low Moonrock','Air max 90 Off white','Air presto Off white','Air vapormax Off white',
'Jordan 1 retro high Off white','Blazer mid Off white','350 Low Pirate Black','350 Low Oxford Tan','350 Low Turtledove',
'350 Low Pirate Black 2015', '350 V2 SemiFrozen','Air force 1 Off white','Air max 97 Off white','Air force 1 low virgil AF100',
'React hyperdrunk 2017 Off white', 'Zoom fly Off white','350 V2 Beluga 2.0','350 V2 Blue Tint','Vapor max Off white 2018','Jordan 1 retro high Off White',
'Air vapormax Off white black','Jordan 1 retro high Off white Uni blue', 'Air presto Off white black 2018','Air presto Off white white 2018',
'ZoomFly mercurial Off white black','ZoomFly mercurial Off white T Orange','350 V2 Butter','Air max 97 Off white Elemental RQueen',
'Blazer mid Off white all H eves', 'Blazer mid Off white grim reaper', '350 V2 Sesame','Blazer mid Off white w grey','Air max 97 Off white menta',
'Air max 97 Off white black','ZoomFly Off white black silver','ZoomFly Off white pink','Air force 1 low white volt','Air force 1 low white black white',
'350 V2 Static','350 V2 Static-Reflective','Air max 90 Off white black','Air max 90 Off white desert ore')
# -- Marca
Sneaker_brand <- unique(Datos$Brand_u)
# -- Region
Buyer_region <- unique(Datos$Buyer_Region)
# -- Talla
Sneaker_size <- unique(Datos$Shoe_Size)
# -- Generamos un DataFrame por modelo de sneaker
my_keys <- Sneaker_models
DF_sneaker_model <- vector(mode="list", length=length(my_keys))
names(DF_sneaker_model) <- my_keys
for (i in 1:length(Sneaker_models)){
DF_sneaker_model[[i]] <- Datos[Datos$Sneaker_Name %in% Sneaker_models[i],]
}
# -- Calculamos rendimientos diarios y vemos cual es el día de la semana con mayor rendimiento
my_keys <- Sneaker_models
Rendimientos <- vector(mode="list", length=length(my_keys))
names(Rendimientos) <- my_keys
d_rends <- c() # Rendimientos diarios maximos
sd_drends <- c()
t_rends <- c() # Rendimiento total del periodo
sd_trends <- c()
max_rend <- c()
min_rend <- c()
day_max <- c()
day_min <- c()
for (i in 1:length(Sneaker_models)){
DF_new <- DF_sneaker_model[[i]]$Sale_Price
rends <- diff(DF_new)/DF_new[-length(DF_new)]
temp_dataframe <- data.frame(rends, DF_sneaker_model[[i]]$Order_Date[1:length(DF_sneaker_model[[i]]$Order_Date)-1], DF_sneaker_model[[i]]$Wee_day_order[1:length(DF_sneaker_model[[i]]$Wee_day_order)-1])
colnames(temp_dataframe) <- c('Rends', 'Date', 'Week_day')
Rendimientos[[i]] <- temp_dataframe
d_rends[i] <- max(rends)
t_rends[i] <- (last(DF_new)-first(DF_new))/first(DF_new)
week_days <- unique(Rendimientos[[i]]$Week_day)
prom_week_days <- c()
data_week <- data.frame(matrix(nrow = 2, ncol = length(week_days)))
colnames(data_week) <- week_days
rownames(data_week) <- c('Mean', 'Sd')
for (j in 1:length(week_days)){
index <- which(Rendimientos[[i]]$Week_day==week_days[j])
prom <- mean(Rendimientos[[i]]$Rends[index])
sd <- sd(Rendimientos[[i]]$Rends[index])
data_week[1,j] <- prom
data_week[2,j] <- sd
}
max_rend[i] <- max(data_week[1,])
min_rend[i] <- min(data_week[1,])
id <- which(data_week[1,]==max(data_week[1,]))
id_min <- which(data_week[1,]==min(data_week[1,]))
day_max[i] <- colnames(data_week)[id]
day_min[i] <- colnames(data_week)[id_min]
}
Una vez obtenidas todas las ventas normalizadas de las empresas, en la siguiente linea de codigo se graficaran las cuales se guardaran en la lista de graficos “p”:
# Graficamos dia de la semana con mayor rendimiento y falta el sneaker
# Top 3 días con mayor rendimiento
tt <- sort(table(day_max))
Day <- c(names(tt[length(tt)]), names(tt[length(tt)-1]), names(tt[length(tt)-2]))
Rep <- c(tt[length(tt)], tt[length(tt)-1], tt[length(tt)-2])
data <- data.frame(Day, Rep)
p <- plot_ly(data, x = ~Day, y = ~Rep, type = 'bar', name = 'Return') %>%
layout(title = 'Top 3 days (High Daily Return)',yaxis = list(title = 'Repeat'), barmode = 'group')
# Top 3 días con menor rendimiento
tt <- sort(table(day_min))
Day <- c(names(tt[length(tt)]), names(tt[length(tt)-1]), names(tt[length(tt)-2]))
Rep <- c(tt[length(tt)], tt[length(tt)-1], tt[length(tt)-2])
data <- data.frame(Day, Rep)
p <- plot_ly(data, x = ~Day, y = ~Rep, type = 'bar', name = 'Return') %>%
layout(title = 'Top 3 days (Low Daily Return)',yaxis = list(title = 'Repeat'), barmode = 'group')
# Top 3 sneakers con mayor rendimiento de todo el periodo
tt <- sort(t_rends)
id1 <- which(t_rends == tt[length(tt)])
id2 <- which(t_rends == tt[length(tt)-1])
id3 <- which(t_rends == tt[length(tt)-2])
Sneaker <- c(Sneaker_models_abb[id1], Sneaker_models_abb[id2], Sneaker_models_abb[id3])
Rend <- c(tt[length(tt)], tt[length(tt)-1], tt[length(tt)-2])*100
data <- data.frame(Sneaker, Rend)
p <- plot_ly(data, x = ~Sneaker, y = ~Rend, type = 'bar', name = 'Return') %>%
layout(title = 'Top 3 Sneaker (Return of the period)',yaxis = list(title = 'Return %'), barmode = 'group')
# -- Generamos un DataFrame por marca
my_keys <- Sneaker_brand
DF_brand <- vector(mode="list", length=length(my_keys))
names(DF_brand) <- my_keys
for (i in 1:length(Sneaker_brand)){
DF_brand[[i]] <- Datos[Datos$Brand_u %in% Sneaker_brand[i],]
}
# -- Generamos un DataFrame por region del comprador
my_keys <- Buyer_region
DF_region <- vector(mode="list", length=length(my_keys))
names(DF_region) <- my_keys
for (i in 1:length(Buyer_region)){
DF_region[[i]] <- Datos[Datos$Buyer_Region %in% Buyer_region[i],]
}
# -- Generamos un DataFrame por talla
my_keys <- Sneaker_size
DF_size <- vector(mode="list", length=length(my_keys))
names(DF_size) <- my_keys
for (i in 1:length(Sneaker_size)){
DF_size[[i]] <- Datos[Datos$Shoe_Size %in% Sneaker_size[i],]
}
# ----------------------------- GRAFICAMOS SERIES DE TIEMPO -----------------------------
# Graficamos cada modelo de sneaker con todas sus tallas
model <- Sneaker_models[1]
model_abb <- Sneaker_models_abb[1]
sizes <- unique(DF_sneaker_model[[model]]$Shoe_Size)
size <- sizes[1]
index_size <- which(DF_size[[paste(size,sep=" ")]]$Sneaker_Name==Sneaker_models[1])
my_x <- DF_size[[paste(size,sep=" ")]]$Order_Date[index_size]
my_y <- DF_size[[paste(size,sep=" ")]]$Sale_Price[index_size]
# Let's do a first plot
p<-plot_ly(y=my_y, x=my_x , type="scatter", mode="lines", name = size, evaluate = TRUE) %>%
layout(
title = paste("Sale Price by Size", model_abb,sep=" "),
xaxis = list(title = "Date"),
yaxis = list(title = "Sale price"))
# Add 9 trace to this graphic with a loop!
for(i in 2:length(sizes)){
size <- sizes[i]
index_size <- which(DF_size[[paste(size,sep=" ")]]$Sneaker_Name==Sneaker_models[i])
my_x = DF_size[[paste(size,sep=" ")]]$Order_Date[index_size]
my_y = DF_size[[paste(size,sep=" ")]]$Sale_Price[index_size]
p <- add_trace(p, x=my_x, y=my_y, type="scatter", mode="lines", name = sizes[i], evaluate = TRUE)
}
# Graficamos con grafica de barras
items <- c()
for (i in 1:length(sizes)){
size <- sizes[i]
items[i] <- length(which(DF_size[[paste(size,sep=" ")]]$Sneaker_Name==model))
}
p <- plot_ly(x = sizes, y = items, type = "bar", evaluate = TRUE)%>%
layout(
title = paste("Sales by Size", model_abb,sep=" "),
xaxis = list(title = "Size"),
yaxis = list(title = "Sales"))
# -- Graficamos los Sneakers más vendidos
items <- c()
for (i in 1:length(Sneaker_models_abb)){
items[i] <- nrow(DF_sneaker_model[[i]])
}
p <- plot_ly(x = Sneaker_models_abb, y = items, type = "bar", evaluate = TRUE)%>%
layout(
title = paste("Sales by Sneaker"),
xaxis = list(title = "Sneaker"),
yaxis = list(title = "Sales"))
# -- Grafica de pastel
#USPersonalExpenditure <- data.frame("Categorie"=rownames(USPersonalExpenditure), USPersonalExpenditure)
Nike <- length(DF_brand$Nike$Order_Date)
Adidas <- length(DF_brand$Adidas$Order_Date)
sales <- c(Adidas,Nike)
brands <- c('Adidas', 'Nike')
data <- data.frame('Brand' = brands, 'Sales'= sales)
p <- plot_ly(data, labels = ~Brand, values = ~Sales, type = 'pie') %>%
layout(title = 'Sales by Brand',
xaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE),
yaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE))
# -- Promedios de precio de venta contra retail de cada marca
Nike_retail_prom <- mean(DF_brand$Nike$Retail_Price)
Nike_sale_prom <- mean(DF_brand$Nike$Sale_Price)
Adidas_retail_prom <- mean(DF_brand$Adidas$Retail_Price)
Adidas_sale_prom <- mean(DF_brand$Adidas$Sale_Price)
Brand <- c("Adidas", "Nike")
Retail <- c(Adidas_retail_prom,Nike_retail_prom)
Sale <- c(Adidas_sale_prom,Nike_sale_prom)
data <- data.frame(Brand, Retail, Sale)
p <- plot_ly(data, x = ~Brand, y = ~Retail, type = 'bar', name = 'Retail Price') %>%
add_trace(y = ~Sale, name = 'Sale Price') %>%
layout(title = 'Average Retail & Sale Price by Brand',yaxis = list(title = 'Sale Price'), barmode = 'group')
# Marcas más vendida por Region
items_n <- c()
items_a <- c()
for (i in 1:length(Buyer_region)){
items_n[i] <- length(which(DF_region[[paste(Buyer_region[i],sep=" ")]]$Brand_u=='Nike'))
items_a[i] <- length(which(DF_region[[paste(Buyer_region[i],sep=" ")]]$Brand_u=='Adidas'))
}
Region <- Buyer_region
Nike <- items_n
Adidas <- items_a
data <- data.frame(Region, Nike, Adidas)
p <- plot_ly(data, x = ~Region, y = ~Nike, type = 'bar', name = 'Nike') %>%
add_trace(y = ~Adidas, name = 'Adidas') %>%
layout(title = 'Sales by Region Buyer',yaxis = list(title = 'Sale Price'), barmode = 'group')
# Graficas de los top 3 regiones y top 3 tallas y top 3 sneakers
# Top 3 region
tt <- sort(table(Datos$Buyer_Region))
Region <- c(names(tt[length(tt)]), names(tt[length(tt)-1]), names(tt[length(tt)-2]))
Sales <- c(tt[length(tt)], tt[length(tt)-1], tt[length(tt)-2])
data <- data.frame(Region, Sales)
p <- plot_ly(data, x = ~Region, y = ~Sales, type = 'bar', name = 'Sales') %>%
layout(title = 'Top 3 # of Sales by Region',yaxis = list(title = 'Sales'), barmode = 'group')
#Top 3 tallas
tt <- sort(table(Datos$Shoe_Size))
Sizing <- c(names(tt[length(tt)]), names(tt[length(tt)-1]), names(tt[length(tt)-2]))
Sales <- c(tt[length(tt)], tt[length(tt)-1], tt[length(tt)-2])
data <- data.frame(Sizing, Sales)
p <- plot_ly(data, x = ~Sizing, y = ~Sales, type = 'bar', name = 'Sales') %>%
layout(title = 'Top 3 # of Sales by Size',yaxis = list(title = 'Sales'), barmode = 'group')
#Top 3 Sneakers
tt <- sort(table(Datos$Sneaker_Name))
Sneakerb <- c(names(tt[length(tt)]), names(tt[length(tt)-1]), names(tt[length(tt)-2]))
Sales <- c(tt[length(tt)], tt[length(tt)-1], tt[length(tt)-2])
data <- data.frame(Sneakerb, Sales)
Sneaker <- c(Sneaker_models_abb[25], Sneaker_models_abb[35], Sneaker_models_abb[8])
p <- plot_ly(data, x = ~Sneaker, y = ~Sales, type = 'bar', name = 'Sales') %>%
layout(title = 'Top 3 # of Sales by Sneaker',yaxis = list(title = 'Sales'), barmode = 'group')
LS0tCnRpdGxlOiAiU3RvY2sgWCIKYXV0aG9yOiBBbGVqYW5kcm8gUmFtb3MgR3V0aWVycmV6CmRhdGU6ICcxMSBkZSBEaWNpZW1icmUgZGVsIDIwMTcnCm91dHB1dDogCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQKLS0tCkFCU1RSQUNUOiAKRW4gZWwgcHJlc2VudGUgZG9jdW1lbnRvIHNlIHByZXNlbnRhIGVsIHRyYWJham8gcXVlIHJlYWxpY2UgZHVyYW50ZSBtaSBlc3RhbmNpYSBlbiBsYSBiZWNhcmlhIGRlIGxhIEVzY3VlbGEgZGUgTmVnb2Npb3MgZGVsIElURVNPLCBsYSBjdWFsIGZ1ZSBjb29yZGluYWRhIHBvciBlbCBwcm9mZXNvcjogSm9zZSBEZWxhQ2VyZGEgR2FzdGVsdW0geSBsYSBhc2lzdGVudGUgZGUgZGlyZWNjaW9uOiBEb3JhIEFsaWNpYSBDYXN0YW5lZGEgQ29yb25hLiAKCi0gKipOb21icmUgZGVsIGFsdW1ubzoqKiAqQWxlamFuZHJvIFJhbW9zIEd0ei4qCi0gKipGZWNoYToqKiAqMjIgZGUgRmVicmVybyBkZWwgMjAxOSoKCjxicj4KCiMjIyMgQW5hbGlzaXMgRXN0YWRpc3RpY28gLyBDb24gUiAKCiRJbmljaWFtb3MkICRjb24kICRlbCQgJGNvZGlnbzokCgpTZSBjYXJnYW4gbGFzIHBhcXVldGVyaWFzIGNvbiBsYXMgY3VhbGVzIHRyYWJhamFyYSBlbCBwcm9ncmFtYSwgc2kgZXN0YXMgbm8gc2UgY2FyZ2FuLCBwdWVkZW4gcHJlc2VudGFyc2UgbXV0bGlwbGVzIGZhbGxhcyB5YSBxdWUgc29uIG5lY2VzYXJpYXMgcGFyYSBvYnRlbmVyIGNpZXJ0b3MgcmVzdWx0YWRvcy4gCjxicj4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIFN0b2NrWCAKCiMgQ2FyZ2Ftb3MgbGFzIHBhcXVldGVyw61hcyBuZWNlc2FyaWFzCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShnZ3Bsb3QyKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KHBsb3RseSkpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShRdWFuZGwpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoVFRSKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KFBvcnRmb2xpb0FuYWx5dGljcykpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShxdWFudG1vZCkpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShQZXJmb3JtYW5jZUFuYWx5dGljcykpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShyZWFkeGwpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkocGxvdGx5KSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KE1BU1MpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoRGlzY3JldGVMYXBsYWNlKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KGV4dHJhRGlzdHIpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoc21vb3RobWVzdCkpCgpyZXF1aXJlKHJlc2hhcGUyKQoKYGBgCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKClByaW1lcmFtZW50ZSBlcyBpbXBvcnRhbnRlIG1lbmNpb25hciBxdWUgZWwgZWwgc2V0d2QgZGUgIlIiIGRlYmUgZGUgZXN0YXIgZW4gbGEgdWJpY2FjaW9uIGRlIGxhIGNvbXB1dGFkb3JhIGVuIGxhIGN1YWwgc2UgdGllbmVuIHRvZG9zIGxvcyBkb2N1bWVudG9zIGRlICJFeGNlbCIgY29tbyBzZSBtdWVzdHJhIGEgY29udGludWFjaW9uOiAKCkVuIGVzdGUgY2FzbyBzZSBwcmVzZW50YSBsYSB1YmljYWNpb24gZW4gZG9uZGUgc2UgZW5jb250cmFiYW4gbG9zIGFyY2hpdm9zIGVuIG1pIGNvbXB1dGFkb3JhOgo8YnI+CgpgYGB7cn0KCiMgRWxlZ2ltb3MgZWwgZGlyZWN0b3JpbyBlbiBkb25kZSB2YW1vcyBhIHRyYWJhamFyIApzZXR3ZCgifi9Eb2N1bWVudHMvQUxFSkFORFJPIFJBTU9TIEdVVElFUlJFWi9QUk9ZRUNUT1MvU3RvY2tYIikgIyBEb25kZSBlc3RhbiBudWVzdHJvcyBhcmNoaXZvcyB1YmljYWRvcyBlbiBlbCBQQwoKYGBgCgoKRW4gbGEgc2lndWllbnRlIGxpbmVhIHNlIGxlZSBlbCBhcmNoaXZvIGRlIEV4Y2VsIGRlIGxhcyB2ZW50YXMgKCJQcnVlYmFyIikgeSBzZSBndWFyZGEgZW4gbGEgdmFyaWFibGUgbGxhbWFkYSAiRGF0b3MiIGVsIGN1YWwgZXMgY29udmVydGlkbyBhIHVuIERhdGFGcmFtZSwgdW4gdGlwbyBkZSBhcmNoaXZvIGVuICJSIiBxdWUgZW11bGEgdW5hIGhvamEgZGUgIkV4Y2VsIiB5IGhhY2UgZmFjaWwgc3UgbWFuaXB1bGFjaW9uIHkgYW5hbGlzaXM6Cjxicj4KCmBgYHtyfQoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIElNUE9SVEFSIERBVE9TLCBBQ09NT0RPIERFIERBVE9TIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyAtLSBMZWVtb3MgbGEgYmFzZSBkZSBkYXRvcyBkZSBsYXMgdmVudGFzIGRlIGxhcyBFbXByZXNhcwpEYXRvcyA8LSByZWFkeGw6OnJlYWRfZXhjZWwoIlN0b2NreC54bHN4Iiwgc2hlZXQgPSAxKQpEYXRvcyA8LSBhcy5kYXRhLmZyYW1lKERhdG9zKQoKCmBgYAoKQSBjb250aW51YWNpb24sIGNvbW8gbGEgYmFzZSBkZSBkYXRvcyBsZWlkYSBjb250aWVuZSB1biAwIGVuIGRvbmRlIG5vIHNlIHRpZW5lbiByZWdpc3RybyBkZSBsYXMgdmVudGFzLCBlc3RvcyBzb24gY29udmVydGlkb3MgZW4gTkEgdmFsdWVzLCBlcyBkZWNpciBlbiB2YWxvcmVzIG5vIGRpc3BvbmlibGVzLCBkZSBlc3RhIG1hbmVyYSBldml0YW1vcyByZWFsaXphciB1biBhbmFsaXNpcyBlcnJvbmVvIGRlIGxvcyBkYXRvcy4gCgpQb3N0ZXJpb3JtZW50ZSwgY29tbyBlbiAiRGF0b3MiIHRhbWJpZW4gc2UgZW5jb250cmFiYW4gbGFzIHZhcmlhYmxlcyAiTkFJQ1MiLCAiUFJPUElFREFEIiB5ICJSRUdJU1RSTyIgZGUgY2FkYSB1bmEgZGUgbGFzIGVtcHJlc2FzIHkgcHVkaWVyYW4gcHJlc2VudGFyIGFsZ3VuIHJ1aWRvIGVuIGxvcyBkYXRvcyBjdWFuZG8gc2UgdHJhYmFqYXJhIGNvbiBlbGxvcywgZXN0YXMgZnVlcm9uIGVsaW1pbmFkYXMgZGUgbGEgdmFyaWFibGUgIkRhdG9zX2FudWFsIiB5IGRlIGVzdGEgbWFuZXJhIHNlIHF1ZWRhcmlhbiB1bmljYW1lbnRlIGxhcyB2ZW50YXMgZGUgbGFzIGVtcHJlc2FzLiAKPGJyPgpgYGB7cn0KCiMgLS0gTW9kZWxvcyBkZSB0ZW5pcyAKU25lYWtlcl9tb2RlbHMgPC0gdW5pcXVlKERhdG9zJFNuZWFrZXJfTmFtZSkKU25lYWtlcl9tb2RlbHNfYWJiIDwtIGMoJzM1MCBWMiBCZWx1Z2EnLCczNTAgVjIgQmxhY2sgQ29vcGVyJywnMzUwIFYyIEJsYWNrIEdyZWVuJywnMzUwIFYyIEJsYWNrIFJlZCcsJzM1MCBWMiBCbGFjayBSZWQgMjAxNycsJzM1MCBWMiBCbGFjayBXaGl0ZScsCiAgICAgICAgICAgICAgICAgICAgICAgICczNTAgVjIgQ3JlYW0gV2hpdGUnLCczNTAgVjIgWmVicmEnLCczNTAgTG93IE1vb25yb2NrJywnQWlyIG1heCA5MCBPZmYgd2hpdGUnLCdBaXIgcHJlc3RvIE9mZiB3aGl0ZScsJ0FpciB2YXBvcm1heCBPZmYgd2hpdGUnLAogICAgICAgICAgICAgICAgICAgICAgICAnSm9yZGFuIDEgcmV0cm8gaGlnaCBPZmYgd2hpdGUnLCdCbGF6ZXIgbWlkIE9mZiB3aGl0ZScsJzM1MCBMb3cgUGlyYXRlIEJsYWNrJywnMzUwIExvdyBPeGZvcmQgVGFuJywnMzUwIExvdyBUdXJ0bGVkb3ZlJywKICAgICAgICAgICAgICAgICAgICAgICAgJzM1MCBMb3cgUGlyYXRlIEJsYWNrIDIwMTUnLCAnMzUwIFYyIFNlbWlGcm96ZW4nLCdBaXIgZm9yY2UgMSBPZmYgd2hpdGUnLCdBaXIgbWF4IDk3IE9mZiB3aGl0ZScsJ0FpciBmb3JjZSAxIGxvdyB2aXJnaWwgQUYxMDAnLAogICAgICAgICAgICAgICAgICAgICAgICAnUmVhY3QgaHlwZXJkcnVuayAyMDE3IE9mZiB3aGl0ZScsICdab29tIGZseSBPZmYgd2hpdGUnLCczNTAgVjIgQmVsdWdhIDIuMCcsJzM1MCBWMiBCbHVlIFRpbnQnLCdWYXBvciBtYXggT2ZmIHdoaXRlIDIwMTgnLCdKb3JkYW4gMSByZXRybyBoaWdoIE9mZiBXaGl0ZScsCiAgICAgICAgICAgICAgICAgICAgICAgICdBaXIgdmFwb3JtYXggT2ZmIHdoaXRlIGJsYWNrJywnSm9yZGFuIDEgcmV0cm8gaGlnaCBPZmYgd2hpdGUgVW5pIGJsdWUnLCAnQWlyIHByZXN0byBPZmYgd2hpdGUgYmxhY2sgMjAxOCcsJ0FpciBwcmVzdG8gT2ZmIHdoaXRlIHdoaXRlIDIwMTgnLAogICAgICAgICAgICAgICAgICAgICAgICAnWm9vbUZseSBtZXJjdXJpYWwgT2ZmIHdoaXRlIGJsYWNrJywnWm9vbUZseSBtZXJjdXJpYWwgT2ZmIHdoaXRlIFQgT3JhbmdlJywnMzUwIFYyIEJ1dHRlcicsJ0FpciBtYXggOTcgT2ZmIHdoaXRlIEVsZW1lbnRhbCBSUXVlZW4nLAogICAgICAgICAgICAgICAgICAgICAgICAnQmxhemVyIG1pZCBPZmYgd2hpdGUgYWxsIEggZXZlcycsICdCbGF6ZXIgbWlkIE9mZiB3aGl0ZSBncmltIHJlYXBlcicsICczNTAgVjIgU2VzYW1lJywnQmxhemVyIG1pZCBPZmYgd2hpdGUgdyBncmV5JywnQWlyIG1heCA5NyBPZmYgd2hpdGUgbWVudGEnLAogICAgICAgICAgICAgICAgICAgICAgICAnQWlyIG1heCA5NyBPZmYgd2hpdGUgYmxhY2snLCdab29tRmx5IE9mZiB3aGl0ZSBibGFjayBzaWx2ZXInLCdab29tRmx5IE9mZiB3aGl0ZSBwaW5rJywnQWlyIGZvcmNlIDEgbG93IHdoaXRlIHZvbHQnLCdBaXIgZm9yY2UgMSBsb3cgd2hpdGUgYmxhY2sgd2hpdGUnLAogICAgICAgICAgICAgICAgICAgICAgICAnMzUwIFYyIFN0YXRpYycsJzM1MCBWMiBTdGF0aWMtUmVmbGVjdGl2ZScsJ0FpciBtYXggOTAgT2ZmIHdoaXRlIGJsYWNrJywnQWlyIG1heCA5MCBPZmYgd2hpdGUgZGVzZXJ0IG9yZScpCgojIC0tIE1hcmNhIApTbmVha2VyX2JyYW5kIDwtIHVuaXF1ZShEYXRvcyRCcmFuZF91KQoKIyAtLSBSZWdpb24KQnV5ZXJfcmVnaW9uIDwtIHVuaXF1ZShEYXRvcyRCdXllcl9SZWdpb24pCgojIC0tIFRhbGxhClNuZWFrZXJfc2l6ZSA8LSB1bmlxdWUoRGF0b3MkU2hvZV9TaXplKQoKIyAtLSBHZW5lcmFtb3MgdW4gRGF0YUZyYW1lIHBvciBtb2RlbG8gZGUgc25lYWtlciAKbXlfa2V5cyA8LSBTbmVha2VyX21vZGVscwpERl9zbmVha2VyX21vZGVsIDwtIHZlY3Rvcihtb2RlPSJsaXN0IiwgbGVuZ3RoPWxlbmd0aChteV9rZXlzKSkKbmFtZXMoREZfc25lYWtlcl9tb2RlbCkgPC0gbXlfa2V5cwoKZm9yIChpIGluIDE6bGVuZ3RoKFNuZWFrZXJfbW9kZWxzKSl7CiAgCiAgREZfc25lYWtlcl9tb2RlbFtbaV1dIDwtIERhdG9zW0RhdG9zJFNuZWFrZXJfTmFtZSAlaW4lIFNuZWFrZXJfbW9kZWxzW2ldLF0KICAKfQoKIyAtLSBDYWxjdWxhbW9zIHJlbmRpbWllbnRvcyBkaWFyaW9zIHkgdmVtb3MgY3VhbCBlcyBlbCBkw61hIGRlIGxhIHNlbWFuYSBjb24gbWF5b3IgcmVuZGltaWVudG8KbXlfa2V5cyA8LSBTbmVha2VyX21vZGVscwpSZW5kaW1pZW50b3MgPC0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGg9bGVuZ3RoKG15X2tleXMpKQpuYW1lcyhSZW5kaW1pZW50b3MpIDwtIG15X2tleXMKZF9yZW5kcyA8LSBjKCkgIyBSZW5kaW1pZW50b3MgZGlhcmlvcyBtYXhpbW9zCnNkX2RyZW5kcyA8LSBjKCkKdF9yZW5kcyA8LSBjKCkgIyBSZW5kaW1pZW50byB0b3RhbCBkZWwgcGVyaW9kbyAKc2RfdHJlbmRzIDwtIGMoKSAKbWF4X3JlbmQgPC0gYygpCm1pbl9yZW5kIDwtIGMoKQpkYXlfbWF4IDwtIGMoKQpkYXlfbWluIDwtIGMoKQoKZm9yIChpIGluIDE6bGVuZ3RoKFNuZWFrZXJfbW9kZWxzKSl7CgogIERGX25ldyA8LSBERl9zbmVha2VyX21vZGVsW1tpXV0kU2FsZV9QcmljZQogIHJlbmRzIDwtIGRpZmYoREZfbmV3KS9ERl9uZXdbLWxlbmd0aChERl9uZXcpXQogIHRlbXBfZGF0YWZyYW1lIDwtIGRhdGEuZnJhbWUocmVuZHMsIERGX3NuZWFrZXJfbW9kZWxbW2ldXSRPcmRlcl9EYXRlWzE6bGVuZ3RoKERGX3NuZWFrZXJfbW9kZWxbW2ldXSRPcmRlcl9EYXRlKS0xXSwgREZfc25lYWtlcl9tb2RlbFtbaV1dJFdlZV9kYXlfb3JkZXJbMTpsZW5ndGgoREZfc25lYWtlcl9tb2RlbFtbaV1dJFdlZV9kYXlfb3JkZXIpLTFdKQogIGNvbG5hbWVzKHRlbXBfZGF0YWZyYW1lKSA8LSBjKCdSZW5kcycsICdEYXRlJywgJ1dlZWtfZGF5JykKICBSZW5kaW1pZW50b3NbW2ldXSA8LSB0ZW1wX2RhdGFmcmFtZQogIGRfcmVuZHNbaV0gPC0gbWF4KHJlbmRzKQogIHRfcmVuZHNbaV0gPC0gKGxhc3QoREZfbmV3KS1maXJzdChERl9uZXcpKS9maXJzdChERl9uZXcpCiAgd2Vla19kYXlzIDwtIHVuaXF1ZShSZW5kaW1pZW50b3NbW2ldXSRXZWVrX2RheSkgCiAgcHJvbV93ZWVrX2RheXMgPC0gYygpCiAgZGF0YV93ZWVrIDwtIGRhdGEuZnJhbWUobWF0cml4KG5yb3cgPSAyLCBuY29sID0gbGVuZ3RoKHdlZWtfZGF5cykpKQogIGNvbG5hbWVzKGRhdGFfd2VlaykgPC0gd2Vla19kYXlzCiAgcm93bmFtZXMoZGF0YV93ZWVrKSA8LSBjKCdNZWFuJywgJ1NkJykKICAKICBmb3IgKGogaW4gMTpsZW5ndGgod2Vla19kYXlzKSl7CiAgICAKICAgIGluZGV4IDwtIHdoaWNoKFJlbmRpbWllbnRvc1tbaV1dJFdlZWtfZGF5PT13ZWVrX2RheXNbal0pCiAgICBwcm9tIDwtIG1lYW4oUmVuZGltaWVudG9zW1tpXV0kUmVuZHNbaW5kZXhdKQogICAgc2QgPC0gc2QoUmVuZGltaWVudG9zW1tpXV0kUmVuZHNbaW5kZXhdKQogICAgZGF0YV93ZWVrWzEsal0gPC0gcHJvbQogICAgZGF0YV93ZWVrWzIsal0gPC0gc2QKCiAgfQogIAogIG1heF9yZW5kW2ldIDwtIG1heChkYXRhX3dlZWtbMSxdKQogIG1pbl9yZW5kW2ldIDwtIG1pbihkYXRhX3dlZWtbMSxdKQogIGlkIDwtIHdoaWNoKGRhdGFfd2Vla1sxLF09PW1heChkYXRhX3dlZWtbMSxdKSkKICBpZF9taW4gPC0gd2hpY2goZGF0YV93ZWVrWzEsXT09bWluKGRhdGFfd2Vla1sxLF0pKQogIGRheV9tYXhbaV0gPC0gY29sbmFtZXMoZGF0YV93ZWVrKVtpZF0KICBkYXlfbWluW2ldIDwtIGNvbG5hbWVzKGRhdGFfd2VlaylbaWRfbWluXQoKfQoKCmBgYAoKVW5hIHZleiBvYnRlbmlkYXMgdG9kYXMgbGFzIHZlbnRhcyBub3JtYWxpemFkYXMgZGUgbGFzIGVtcHJlc2FzLCBlbiBsYSBzaWd1aWVudGUgbGluZWEgZGUgY29kaWdvIHNlIGdyYWZpY2FyYW4gbGFzIGN1YWxlcyBzZSBndWFyZGFyYW4gZW4gbGEgbGlzdGEgZGUgZ3JhZmljb3MgInAiOgo8YnI+CgpgYGB7cn0KIyBHcmFmaWNhbW9zIGRpYSBkZSBsYSBzZW1hbmEgY29uIG1heW9yIHJlbmRpbWllbnRvIHkgZmFsdGEgZWwgc25lYWtlciAgCgojIFRvcCAzIGTDrWFzIGNvbiBtYXlvciByZW5kaW1pZW50byAKdHQgPC0gc29ydCh0YWJsZShkYXlfbWF4KSkKCkRheSA8LSBjKG5hbWVzKHR0W2xlbmd0aCh0dCldKSwgbmFtZXModHRbbGVuZ3RoKHR0KS0xXSksIG5hbWVzKHR0W2xlbmd0aCh0dCktMl0pKQpSZXAgPC0gYyh0dFtsZW5ndGgodHQpXSwgdHRbbGVuZ3RoKHR0KS0xXSwgdHRbbGVuZ3RoKHR0KS0yXSkKZGF0YSA8LSBkYXRhLmZyYW1lKERheSwgUmVwKQoKcCA8LSBwbG90X2x5KGRhdGEsIHggPSB+RGF5LCB5ID0gflJlcCwgdHlwZSA9ICdiYXInLCBuYW1lID0gJ1JldHVybicpICU+JQogIGxheW91dCh0aXRsZSA9ICdUb3AgMyBkYXlzIChIaWdoIERhaWx5IFJldHVybiknLHlheGlzID0gbGlzdCh0aXRsZSA9ICdSZXBlYXQnKSwgYmFybW9kZSA9ICdncm91cCcpCgoKYGBgCgpgYGB7ciwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCnAKCmBgYAoKCmBgYHtyfQoKIyBUb3AgMyBkw61hcyBjb24gbWVub3IgcmVuZGltaWVudG8gCgp0dCA8LSBzb3J0KHRhYmxlKGRheV9taW4pKQoKRGF5IDwtIGMobmFtZXModHRbbGVuZ3RoKHR0KV0pLCBuYW1lcyh0dFtsZW5ndGgodHQpLTFdKSwgbmFtZXModHRbbGVuZ3RoKHR0KS0yXSkpClJlcCA8LSBjKHR0W2xlbmd0aCh0dCldLCB0dFtsZW5ndGgodHQpLTFdLCB0dFtsZW5ndGgodHQpLTJdKQpkYXRhIDwtIGRhdGEuZnJhbWUoRGF5LCBSZXApCgpwIDwtIHBsb3RfbHkoZGF0YSwgeCA9IH5EYXksIHkgPSB+UmVwLCB0eXBlID0gJ2JhcicsIG5hbWUgPSAnUmV0dXJuJykgJT4lCiAgbGF5b3V0KHRpdGxlID0gJ1RvcCAzIGRheXMgKExvdyBEYWlseSBSZXR1cm4pJyx5YXhpcyA9IGxpc3QodGl0bGUgPSAnUmVwZWF0JyksIGJhcm1vZGUgPSAnZ3JvdXAnKQoKCmBgYAoKYGBge3IsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgpwCgpgYGAKCmBgYHtyfQoKIyBUb3AgMyBzbmVha2VycyBjb24gbWF5b3IgcmVuZGltaWVudG8gZGUgdG9kbyBlbCBwZXJpb2RvCgp0dCA8LSBzb3J0KHRfcmVuZHMpCmlkMSA8LSB3aGljaCh0X3JlbmRzID09IHR0W2xlbmd0aCh0dCldKQppZDIgPC0gd2hpY2godF9yZW5kcyA9PSB0dFtsZW5ndGgodHQpLTFdKQppZDMgPC0gd2hpY2godF9yZW5kcyA9PSB0dFtsZW5ndGgodHQpLTJdKQoKU25lYWtlciA8LSBjKFNuZWFrZXJfbW9kZWxzX2FiYltpZDFdLCBTbmVha2VyX21vZGVsc19hYmJbaWQyXSwgU25lYWtlcl9tb2RlbHNfYWJiW2lkM10pClJlbmQgPC0gYyh0dFtsZW5ndGgodHQpXSwgdHRbbGVuZ3RoKHR0KS0xXSwgdHRbbGVuZ3RoKHR0KS0yXSkqMTAwCmRhdGEgPC0gZGF0YS5mcmFtZShTbmVha2VyLCBSZW5kKQoKcCA8LSBwbG90X2x5KGRhdGEsIHggPSB+U25lYWtlciwgeSA9IH5SZW5kLCB0eXBlID0gJ2JhcicsIG5hbWUgPSAnUmV0dXJuJykgJT4lCiAgbGF5b3V0KHRpdGxlID0gJ1RvcCAzIFNuZWFrZXIgKFJldHVybiBvZiB0aGUgcGVyaW9kKScseWF4aXMgPSBsaXN0KHRpdGxlID0gJ1JldHVybiAlJyksIGJhcm1vZGUgPSAnZ3JvdXAnKQoKCgpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKcAoKYGBgCgpgYGB7cn0KCiMgLS0gR2VuZXJhbW9zIHVuIERhdGFGcmFtZSBwb3IgbWFyY2EgCm15X2tleXMgPC0gU25lYWtlcl9icmFuZApERl9icmFuZCA8LSB2ZWN0b3IobW9kZT0ibGlzdCIsIGxlbmd0aD1sZW5ndGgobXlfa2V5cykpCm5hbWVzKERGX2JyYW5kKSA8LSBteV9rZXlzCgpmb3IgKGkgaW4gMTpsZW5ndGgoU25lYWtlcl9icmFuZCkpewogIAogIERGX2JyYW5kW1tpXV0gPC0gRGF0b3NbRGF0b3MkQnJhbmRfdSAlaW4lIFNuZWFrZXJfYnJhbmRbaV0sXQogIAp9CgojIC0tIEdlbmVyYW1vcyB1biBEYXRhRnJhbWUgcG9yIHJlZ2lvbiBkZWwgY29tcHJhZG9yIApteV9rZXlzIDwtIEJ1eWVyX3JlZ2lvbgpERl9yZWdpb24gPC0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGg9bGVuZ3RoKG15X2tleXMpKQpuYW1lcyhERl9yZWdpb24pIDwtIG15X2tleXMKCmZvciAoaSBpbiAxOmxlbmd0aChCdXllcl9yZWdpb24pKXsKICAKICBERl9yZWdpb25bW2ldXSA8LSBEYXRvc1tEYXRvcyRCdXllcl9SZWdpb24gJWluJSBCdXllcl9yZWdpb25baV0sXQogIAp9CgojIC0tIEdlbmVyYW1vcyB1biBEYXRhRnJhbWUgcG9yIHRhbGxhICAKbXlfa2V5cyA8LSBTbmVha2VyX3NpemUKREZfc2l6ZSA8LSB2ZWN0b3IobW9kZT0ibGlzdCIsIGxlbmd0aD1sZW5ndGgobXlfa2V5cykpCm5hbWVzKERGX3NpemUpIDwtIG15X2tleXMKCmZvciAoaSBpbiAxOmxlbmd0aChTbmVha2VyX3NpemUpKXsKICAKICBERl9zaXplW1tpXV0gPC0gRGF0b3NbRGF0b3MkU2hvZV9TaXplICVpbiUgU25lYWtlcl9zaXplW2ldLF0KICAKfQoKYGBgCgoKYGBge3J9CgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIEdSQUZJQ0FNT1MgU0VSSUVTIERFIFRJRU1QTyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBHcmFmaWNhbW9zIGNhZGEgbW9kZWxvIGRlIHNuZWFrZXIgY29uIHRvZGFzIHN1cyB0YWxsYXMgCgptb2RlbCA8LSBTbmVha2VyX21vZGVsc1sxXQptb2RlbF9hYmIgPC0gU25lYWtlcl9tb2RlbHNfYWJiWzFdCnNpemVzIDwtIHVuaXF1ZShERl9zbmVha2VyX21vZGVsW1ttb2RlbF1dJFNob2VfU2l6ZSkKc2l6ZSAgPC0gc2l6ZXNbMV0KCmluZGV4X3NpemUgPC0gd2hpY2goREZfc2l6ZVtbcGFzdGUoc2l6ZSxzZXA9IiAiKV1dJFNuZWFrZXJfTmFtZT09U25lYWtlcl9tb2RlbHNbMV0pCgpteV94IDwtIERGX3NpemVbW3Bhc3RlKHNpemUsc2VwPSIgIildXSRPcmRlcl9EYXRlW2luZGV4X3NpemVdCm15X3kgPC0gREZfc2l6ZVtbcGFzdGUoc2l6ZSxzZXA9IiAiKV1dJFNhbGVfUHJpY2VbaW5kZXhfc2l6ZV0KCiMgTGV0J3MgZG8gYSBmaXJzdCBwbG90CnA8LXBsb3RfbHkoeT1teV95LCB4PW15X3ggLCB0eXBlPSJzY2F0dGVyIiwgbW9kZT0ibGluZXMiLCAgbmFtZSA9IHNpemUsIGV2YWx1YXRlID0gVFJVRSkgJT4lCiAgbGF5b3V0KAogICAgdGl0bGUgPSBwYXN0ZSgiU2FsZSBQcmljZSBieSBTaXplIiwgbW9kZWxfYWJiLHNlcD0iICIpLAogICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIkRhdGUiKSwKICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJTYWxlIHByaWNlIikpCgojIEFkZCA5IHRyYWNlIHRvIHRoaXMgZ3JhcGhpYyB3aXRoIGEgbG9vcCEKZm9yKGkgaW4gMjpsZW5ndGgoc2l6ZXMpKXsKICAKICBzaXplIDwtIHNpemVzW2ldCiAgaW5kZXhfc2l6ZSA8LSB3aGljaChERl9zaXplW1twYXN0ZShzaXplLHNlcD0iICIpXV0kU25lYWtlcl9OYW1lPT1TbmVha2VyX21vZGVsc1tpXSkKICAKICBteV94ID0gREZfc2l6ZVtbcGFzdGUoc2l6ZSxzZXA9IiAiKV1dJE9yZGVyX0RhdGVbaW5kZXhfc2l6ZV0KICBteV95ID0gREZfc2l6ZVtbcGFzdGUoc2l6ZSxzZXA9IiAiKV1dJFNhbGVfUHJpY2VbaW5kZXhfc2l6ZV0KICBwIDwtIGFkZF90cmFjZShwLCB4PW15X3gsIHk9bXlfeSwgdHlwZT0ic2NhdHRlciIsIG1vZGU9ImxpbmVzIiwgbmFtZSA9IHNpemVzW2ldLCBldmFsdWF0ZSA9IFRSVUUpCiAgCn0KCgpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKcAoKYGBgCgpgYGB7cn0KCiMgR3JhZmljYW1vcyBjb24gZ3JhZmljYSBkZSBiYXJyYXMKCml0ZW1zIDwtIGMoKQoKZm9yIChpIGluIDE6bGVuZ3RoKHNpemVzKSl7CiAgCiAgc2l6ZSA8LSBzaXplc1tpXQogIGl0ZW1zW2ldIDwtIGxlbmd0aCh3aGljaChERl9zaXplW1twYXN0ZShzaXplLHNlcD0iICIpXV0kU25lYWtlcl9OYW1lPT1tb2RlbCkpCiAgCn0KCnAgPC0gcGxvdF9seSh4ID0gc2l6ZXMsIHkgPSBpdGVtcywgdHlwZSA9ICJiYXIiLCBldmFsdWF0ZSA9IFRSVUUpJT4lCiAgbGF5b3V0KAogICAgdGl0bGUgPSBwYXN0ZSgiU2FsZXMgYnkgU2l6ZSIsIG1vZGVsX2FiYixzZXA9IiAiKSwKICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJTaXplIiksCiAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiU2FsZXMiKSkKCmBgYAoKYGBge3IsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgpwCgpgYGAKCmBgYHtyfQoKCiMgLS0gR3JhZmljYW1vcyBsb3MgU25lYWtlcnMgbcOhcyB2ZW5kaWRvcyAKCml0ZW1zIDwtIGMoKQoKZm9yIChpIGluIDE6bGVuZ3RoKFNuZWFrZXJfbW9kZWxzX2FiYikpewogIAogIGl0ZW1zW2ldIDwtIG5yb3coREZfc25lYWtlcl9tb2RlbFtbaV1dKQogIAp9CgpwIDwtIHBsb3RfbHkoeCA9IFNuZWFrZXJfbW9kZWxzX2FiYiwgeSA9IGl0ZW1zLCB0eXBlID0gImJhciIsIGV2YWx1YXRlID0gVFJVRSklPiUKICBsYXlvdXQoCiAgICB0aXRsZSA9IHBhc3RlKCJTYWxlcyBieSBTbmVha2VyIiksCiAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiU25lYWtlciIpLAogICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlNhbGVzIikpCgoKYGBgCgpgYGB7ciwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCnAKCmBgYAoKYGBge3J9CgoKIyAtLSBHcmFmaWNhIGRlIHBhc3RlbCAKCiNVU1BlcnNvbmFsRXhwZW5kaXR1cmUgPC0gZGF0YS5mcmFtZSgiQ2F0ZWdvcmllIj1yb3duYW1lcyhVU1BlcnNvbmFsRXhwZW5kaXR1cmUpLCBVU1BlcnNvbmFsRXhwZW5kaXR1cmUpCk5pa2UgPC0gbGVuZ3RoKERGX2JyYW5kJE5pa2UkT3JkZXJfRGF0ZSkKQWRpZGFzIDwtIGxlbmd0aChERl9icmFuZCRBZGlkYXMkT3JkZXJfRGF0ZSkKc2FsZXMgPC0gYyhBZGlkYXMsTmlrZSkKYnJhbmRzIDwtIGMoJ0FkaWRhcycsICdOaWtlJykKZGF0YSA8LSBkYXRhLmZyYW1lKCdCcmFuZCcgPSBicmFuZHMsICdTYWxlcyc9IHNhbGVzKQoKcCA8LSBwbG90X2x5KGRhdGEsIGxhYmVscyA9IH5CcmFuZCwgdmFsdWVzID0gflNhbGVzLCB0eXBlID0gJ3BpZScpICU+JQogIGxheW91dCh0aXRsZSA9ICdTYWxlcyBieSBCcmFuZCcsCiAgICAgICAgIHhheGlzID0gbGlzdChzaG93Z3JpZCA9IEZBTFNFLCB6ZXJvbGluZSA9IEZBTFNFLCBzaG93dGlja2xhYmVscyA9IEZBTFNFKSwKICAgICAgICAgeWF4aXMgPSBsaXN0KHNob3dncmlkID0gRkFMU0UsIHplcm9saW5lID0gRkFMU0UsIHNob3d0aWNrbGFiZWxzID0gRkFMU0UpKQoKCgpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKcAoKYGBgCgpgYGB7cn0KIyAtLSBQcm9tZWRpb3MgZGUgcHJlY2lvIGRlIHZlbnRhIGNvbnRyYSByZXRhaWwgZGUgY2FkYSBtYXJjYSAKCk5pa2VfcmV0YWlsX3Byb20gPC0gbWVhbihERl9icmFuZCROaWtlJFJldGFpbF9QcmljZSkgCk5pa2Vfc2FsZV9wcm9tIDwtIG1lYW4oREZfYnJhbmQkTmlrZSRTYWxlX1ByaWNlKQpBZGlkYXNfcmV0YWlsX3Byb20gPC0gbWVhbihERl9icmFuZCRBZGlkYXMkUmV0YWlsX1ByaWNlKQpBZGlkYXNfc2FsZV9wcm9tIDwtIG1lYW4oREZfYnJhbmQkQWRpZGFzJFNhbGVfUHJpY2UpCgpCcmFuZCA8LSBjKCJBZGlkYXMiLCAiTmlrZSIpClJldGFpbCA8LSBjKEFkaWRhc19yZXRhaWxfcHJvbSxOaWtlX3JldGFpbF9wcm9tKQpTYWxlIDwtIGMoQWRpZGFzX3NhbGVfcHJvbSxOaWtlX3NhbGVfcHJvbSkKZGF0YSA8LSBkYXRhLmZyYW1lKEJyYW5kLCBSZXRhaWwsIFNhbGUpCgpwIDwtIHBsb3RfbHkoZGF0YSwgeCA9IH5CcmFuZCwgeSA9IH5SZXRhaWwsIHR5cGUgPSAnYmFyJywgbmFtZSA9ICdSZXRhaWwgUHJpY2UnKSAlPiUKICBhZGRfdHJhY2UoeSA9IH5TYWxlLCBuYW1lID0gJ1NhbGUgUHJpY2UnKSAlPiUKICBsYXlvdXQodGl0bGUgPSAnQXZlcmFnZSBSZXRhaWwgJiBTYWxlIFByaWNlIGJ5IEJyYW5kJyx5YXhpcyA9IGxpc3QodGl0bGUgPSAnU2FsZSBQcmljZScpLCBiYXJtb2RlID0gJ2dyb3VwJykKCgpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKcAoKYGBgCgpgYGB7cn0KCiMgTWFyY2FzIG3DoXMgdmVuZGlkYSBwb3IgUmVnaW9uIAoKaXRlbXNfbiA8LSBjKCkKaXRlbXNfYSA8LSBjKCkKCmZvciAoaSBpbiAxOmxlbmd0aChCdXllcl9yZWdpb24pKXsKICAKICBpdGVtc19uW2ldIDwtIGxlbmd0aCh3aGljaChERl9yZWdpb25bW3Bhc3RlKEJ1eWVyX3JlZ2lvbltpXSxzZXA9IiAiKV1dJEJyYW5kX3U9PSdOaWtlJykpCiAgaXRlbXNfYVtpXSA8LSBsZW5ndGgod2hpY2goREZfcmVnaW9uW1twYXN0ZShCdXllcl9yZWdpb25baV0sc2VwPSIgIildXSRCcmFuZF91PT0nQWRpZGFzJykpCiAgCn0KClJlZ2lvbiA8LSBCdXllcl9yZWdpb24KTmlrZSA8LSBpdGVtc19uCkFkaWRhcyA8LSBpdGVtc19hCmRhdGEgPC0gZGF0YS5mcmFtZShSZWdpb24sIE5pa2UsIEFkaWRhcykKCnAgPC0gcGxvdF9seShkYXRhLCB4ID0gflJlZ2lvbiwgeSA9IH5OaWtlLCB0eXBlID0gJ2JhcicsIG5hbWUgPSAnTmlrZScpICU+JQogIGFkZF90cmFjZSh5ID0gfkFkaWRhcywgbmFtZSA9ICdBZGlkYXMnKSAlPiUKICBsYXlvdXQodGl0bGUgPSAnU2FsZXMgYnkgUmVnaW9uIEJ1eWVyJyx5YXhpcyA9IGxpc3QodGl0bGUgPSAnU2FsZSBQcmljZScpLCBiYXJtb2RlID0gJ2dyb3VwJykKCgoKYGBgCgpgYGB7ciwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCnAKCmBgYAoKYGBge3J9CgojIEdyYWZpY2FzIGRlIGxvcyB0b3AgMyByZWdpb25lcyB5IHRvcCAzIHRhbGxhcyB5IHRvcCAzIHNuZWFrZXJzCgojIFRvcCAzIHJlZ2lvbgp0dCA8LSBzb3J0KHRhYmxlKERhdG9zJEJ1eWVyX1JlZ2lvbikpCgpSZWdpb24gPC0gYyhuYW1lcyh0dFtsZW5ndGgodHQpXSksIG5hbWVzKHR0W2xlbmd0aCh0dCktMV0pLCBuYW1lcyh0dFtsZW5ndGgodHQpLTJdKSkKU2FsZXMgPC0gYyh0dFtsZW5ndGgodHQpXSwgdHRbbGVuZ3RoKHR0KS0xXSwgdHRbbGVuZ3RoKHR0KS0yXSkKZGF0YSA8LSBkYXRhLmZyYW1lKFJlZ2lvbiwgU2FsZXMpCgpwIDwtIHBsb3RfbHkoZGF0YSwgeCA9IH5SZWdpb24sIHkgPSB+U2FsZXMsIHR5cGUgPSAnYmFyJywgbmFtZSA9ICdTYWxlcycpICU+JQogIGxheW91dCh0aXRsZSA9ICdUb3AgMyAjIG9mIFNhbGVzIGJ5IFJlZ2lvbicseWF4aXMgPSBsaXN0KHRpdGxlID0gJ1NhbGVzJyksIGJhcm1vZGUgPSAnZ3JvdXAnKQoKCgpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKcAoKYGBgCgpgYGB7cn0KCiNUb3AgMyB0YWxsYXMKdHQgPC0gc29ydCh0YWJsZShEYXRvcyRTaG9lX1NpemUpKQoKU2l6aW5nIDwtIGMobmFtZXModHRbbGVuZ3RoKHR0KV0pLCBuYW1lcyh0dFtsZW5ndGgodHQpLTFdKSwgbmFtZXModHRbbGVuZ3RoKHR0KS0yXSkpClNhbGVzIDwtIGModHRbbGVuZ3RoKHR0KV0sIHR0W2xlbmd0aCh0dCktMV0sIHR0W2xlbmd0aCh0dCktMl0pCmRhdGEgPC0gZGF0YS5mcmFtZShTaXppbmcsIFNhbGVzKQoKcCA8LSBwbG90X2x5KGRhdGEsIHggPSB+U2l6aW5nLCB5ID0gflNhbGVzLCB0eXBlID0gJ2JhcicsIG5hbWUgPSAnU2FsZXMnKSAlPiUKICBsYXlvdXQodGl0bGUgPSAnVG9wIDMgIyBvZiBTYWxlcyBieSBTaXplJyx5YXhpcyA9IGxpc3QodGl0bGUgPSAnU2FsZXMnKSwgYmFybW9kZSA9ICdncm91cCcpCgoKYGBgCgpgYGB7ciwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCnAKCmBgYAoKYGBge3J9CgojVG9wIDMgU25lYWtlcnMKdHQgPC0gc29ydCh0YWJsZShEYXRvcyRTbmVha2VyX05hbWUpKQoKU25lYWtlcmIgPC0gYyhuYW1lcyh0dFtsZW5ndGgodHQpXSksIG5hbWVzKHR0W2xlbmd0aCh0dCktMV0pLCBuYW1lcyh0dFtsZW5ndGgodHQpLTJdKSkKU2FsZXMgPC0gYyh0dFtsZW5ndGgodHQpXSwgdHRbbGVuZ3RoKHR0KS0xXSwgdHRbbGVuZ3RoKHR0KS0yXSkKZGF0YSA8LSBkYXRhLmZyYW1lKFNuZWFrZXJiLCBTYWxlcykKU25lYWtlciA8LSBjKFNuZWFrZXJfbW9kZWxzX2FiYlsyNV0sIFNuZWFrZXJfbW9kZWxzX2FiYlszNV0sIFNuZWFrZXJfbW9kZWxzX2FiYls4XSkKCnAgPC0gcGxvdF9seShkYXRhLCB4ID0gflNuZWFrZXIsIHkgPSB+U2FsZXMsIHR5cGUgPSAnYmFyJywgbmFtZSA9ICdTYWxlcycpICU+JQogIGxheW91dCh0aXRsZSA9ICdUb3AgMyAjIG9mIFNhbGVzIGJ5IFNuZWFrZXInLHlheGlzID0gbGlzdCh0aXRsZSA9ICdTYWxlcycpLCBiYXJtb2RlID0gJ2dyb3VwJykKCgpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKcAoKYGBgCg==